#include <gtest/gtest.h>

#include <optional>

class CopyableResource {
public:
	CopyableResource(){}
	CopyableResource(const CopyableResource & other) = default;
	CopyableResource & operator = (const CopyableResource & other) {
		return *this;
	}

	~CopyableResource(){}
	void open(){
		std::cout << "CopyableResource::open" << std::endl;
	}
	void close(){
		std::cout << "CopyableResource::close" << std::endl;
	}
	void hello(){
		std::cout << "CopyableResource::hello" << std::endl;	
	}
};

class MovableResource {
public:
	MovableResource(){}
	MovableResource(MovableResource && other) = default;
	MovableResource & operator = (MovableResource && other) {
		return *this;
	}

	MovableResource(const MovableResource & other) = delete;
	MovableResource & operator = (const MovableResource & other) = delete;

	~MovableResource(){}
	void open(){
		std::cout << "MovableResource::open" << std::endl;
	}
	void close(){
		std::cout << "MovableResource::close" << std::endl;
	}
	void hello(){
		std::cout << "MovableResource::hello" << std::endl;	
	}
};

class DBResource {
public:
	void open(){
		std::cout << "DBResource::open" << std::endl;
	}
	void close(){
		std::cout << "DBResource::close" << std::endl;
	}
	void hello(){
		std::cout << "DBResource::hello" << std::endl;	
	}
private:
};

#include "helper/resource/AutoManaged.h"

using namespace helper::resource;
TEST(AutoManagedTest, test){
	{
		std::cout << "----default--------------" << std::endl;
		CopyableResource cr;
		AutoManaged<CopyableResource> db(cr);
    ASSERT_TRUE(db);
    db.ref().hello();

  
   auto new_db(std::move(db));
   ASSERT_TRUE(new_db);
   ASSERT_TRUE(db);
	}
	{
		std::cout << "------------------" << std::endl;
		AutoManaged<MovableResource&&> db(std::move(MovableResource())); //move
    ASSERT_TRUE(db);
    db.ref().hello();

   auto new_db(std::move(db));
   ASSERT_TRUE(new_db);
   ASSERT_TRUE(db);
   //ASSERT_TRUE(db && db.ref().isMoved())
	}

	{
		std::cout << "------------------" << std::endl;
		DBResource * dbr_ptr = new DBResource;
		AutoManaged<DBResource*> db(dbr_ptr);
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);

		delete dbr_ptr;
	}
	{
    std::cout << "------------------" << std::endl;
		DBResource dbr;
		auto & ref_dbr = dbr;

		AutoManaged<DBResource&> db(ref_dbr);
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_TRUE(db);
	}
	{
		std::cout << "------------------" << std::endl;
		std::unique_ptr<DBResource> dbr_uptr(new DBResource);
		AutoManaged<std::unique_ptr<DBResource>> db(dbr_uptr);//move the dbr_uptr 's ownership
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);
	}
	{
		std::cout << "------------------" << std::endl;
		std::unique_ptr<DBResource> dbr_uptr(new DBResource);
		AutoManaged<std::unique_ptr<DBResource>> db(std::move(dbr_uptr));//move the dbr_uptr 's ownership
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);
	}
	{
		std::cout << "------------------" << std::endl;
		std::shared_ptr<DBResource> dbr_sptr(new DBResource);
		AutoManaged<std::shared_ptr<DBResource>> db(dbr_sptr); //shared the dbr_sptr 's ownership
    ASSERT_TRUE(dbr_sptr); // copy  the source
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);

    ASSERT_TRUE(dbr_sptr); // copy  the source
	}
	{
		std::cout << "------------------" << std::endl;
		std::shared_ptr<DBResource> dbr_sptr(new DBResource);
		AutoManaged<std::shared_ptr<DBResource>> db(std::move(dbr_sptr)); //move dbr_sptr 's ownership
    ASSERT_FALSE(dbr_sptr); //moved;
    ASSERT_TRUE(db);
    db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);
	}
	{
		std::cout << "------------------" << std::endl;
		auto && db = make_auto_managed<DBResource>();
    ASSERT_TRUE(db);
		db.ref().hello();

    auto new_db(std::move(db));
    ASSERT_TRUE(new_db);
    ASSERT_FALSE(db);
	}

}
